package auth

import (
	"car_rental/shared/auth/token"
	"car_rental/shared/id"
	"context"
	"crypto/rsa"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	"io"
	"os"
	"strings"
)

const (
	ImpersonateAccountHeader = "impersonate-account-id"
	authorizationHeader      = "authorization"
	bearerPrefix             = "Bearer "
)

type tokenVerifier interface {
	Verify(token string) (string, error)
}

type interceptor struct {
	publicKey *rsa.PublicKey
	verifier  tokenVerifier
}

type accountIdKey struct {
}

// Interceptor 认证拦截器
func Interceptor(publicKeyFile string) (grpc.UnaryServerInterceptor, error) {

	pubKey, serverInterceptor, err := readPublicKeyFile(publicKeyFile)

	if err != nil {
		return serverInterceptor, err
	}

	i := &interceptor{
		publicKey: pubKey,
		verifier:  &token.JwtTokenVerifier{PublicKey: pubKey},
	}

	return i.HandleReq, nil
}

// 读取公钥文件
func readPublicKeyFile(publicKeyFile string) (*rsa.PublicKey, grpc.UnaryServerInterceptor, error) {

	f, err := os.Open(publicKeyFile)

	if err != nil {
		return nil, nil, fmt.Errorf("无法打开公钥文件: %v", err)
	}

	b, err := io.ReadAll(f)

	if err != nil {
		return nil, nil, fmt.Errorf("无法读取公钥文件: %v", err)
	}

	pubKey, err := jwt.ParseRSAPublicKeyFromPEM(b)

	if err != nil {
		return nil, nil, fmt.Errorf("无法解析公钥文件: %v", err)
	}

	return pubKey, nil, nil

}

// HandleReq 处理请求
func (i *interceptor) HandleReq(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {

	tkn, err := tokenFromContext(ctx)

	if err != nil {
		return nil, status.Error(codes.Unauthenticated, "")
	}

	aid, err := i.verifier.Verify(tkn)

	if err != nil {
		return nil, status.Errorf(codes.Unauthenticated, "token失效: %v", err)
	}

	return handler(ContextWithAccountId(ctx, id.AccountId(aid)), req)

}

// 从Header中取出token
func tokenFromContext(ctx context.Context) (string, error) {

	unauthenticated := status.Error(codes.Unauthenticated, "")

	m, ok := metadata.FromIncomingContext(ctx)

	if !ok {
		return "", unauthenticated
	}

	tkn := ""

	for _, v := range m[authorizationHeader] {
		if strings.HasPrefix(v, bearerPrefix) {
			tkn = v[len(bearerPrefix):]
		}
	}

	if tkn == "" {
		return "", unauthenticated
	}

	return tkn, nil
}

// ContextWithAccountId 创建一个Context给accountId
func ContextWithAccountId(ctx context.Context, aid id.AccountId) context.Context {
	return context.WithValue(ctx, accountIdKey{}, aid)
}

// AccountIdFromContext 从Context中获取accountId
func AccountIdFromContext(ctx context.Context) (id.AccountId, error) {

	unauthenticated := status.Error(codes.Unauthenticated, "")

	v := ctx.Value(accountIdKey{})

	aid, ok := v.(id.AccountId)

	if !ok {
		return "", unauthenticated
	}

	return aid, nil

}
